package UI.Message;

import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Point;
import java.util.concurrent.TimeUnit;

import javax.swing.JTextField;
import javax.swing.JWindow;
import javax.swing.JLabel;

import org.jdesktop.core.animation.timing.Animator;
import org.jdesktop.core.animation.timing.TimingSource;
import org.jdesktop.core.animation.timing.TimingTargetAdapter;
import org.jdesktop.swing.animation.timing.sources.SwingTimerTimingSource;

import com.sun.awt.AWTUtilities;

//@author A0103535R
/******************************* Abstract Class DSPopupMessage ***************************/
public class DSPopupMessage extends JWindow {
	private static final long serialVersionUID = 1L;
	private final int REPEAT_COUNT = 400;
	private final int REPEAT_DURATION = 100;
	private final int OPACITY_INCREASE_COUNT = 50;
	private final int OPACITY_DURATION_COUNT = 350;
	private final int OPACITY_DECREASE_COUNT = 400;
	private final int OPACITY_CHANGE = 5;
	private final int DSPOPUPMESSAGE_TEXT_WIDTH_MINIMUM = 140;
	private final int DSPOPUPMESSAGE_TEXT_HEIGHT = 23;

	private static float _messageAlpha = 255;
	private static int _repeatCount;

	private Animator _animator = null;
	private DSPopupMessageLabel _label = null;

	/**
	 * This is the constructor of DSPopupMessage
	 */
	public DSPopupMessage(Frame owner) {
		super(owner);
		initComponents();
		initAppearance();
	}

	/**
	 * This function is used to initialize its subcomponents
	 */
	private void initComponents() {
		_label = new DSPopupMessageLabel("");
		this.add(_label);
	}

	/**
	 * This function is used to initialize its appearance
	 */
	private void initAppearance() {
		this.setBackground(new Color(0, 0, 0, 0));
		this.setSize(0, 0);
		this.setVisible(false);
	}

	/**
	 * This function is used to show message to user through popupMessage
	 * 
	 * @param message
	 * 			message to show
	 */
	public void showMessage(String message) {
		setMessage(message);
		initAnimation();
		startAnimation();
	}

	/**
	 * This function is used to set message to show through popupMessage
	 * 
	 * @param message
	 * 			message to set
	 */
	public void setMessage(String message) {
		this._label.setText(" " + message);
		this.reLayout();
	}

	/**
	 * This function is used to get the value of alpha of popupMessage
	 * 
	 * @return value of alpha of popupMessage
	 */
	private float getMessageAlpha() {
		return _messageAlpha;
	}

	/**
	 * This function is used to set the alpha value of popupMessage
	 * 
	 * @param a
	 * 			alpha value to set
	 */
	private void setMessageAlpha(float a) {
		this._messageAlpha = a;
	}

	/**
	 * This function is used to increase the alpha value of popupMessage
	 */
	private void increaseAlpha() {
		_messageAlpha += OPACITY_CHANGE;
	}

	/**
	 * This function is used to decrease the alpha value of popupMessage
	 */
	private void decreaseAlpha() {
		_messageAlpha -= OPACITY_CHANGE;
	}

	/**
	 * This function is used to get the count number of repeat
	 * 
	 * @return the count number of repeat
	 */
	private int getRepeatCount() {
		return _repeatCount;
	}

	/**
	 * This function is used to set the count number of repeat
	 * 
	 * @param rc
	 * 			repeat count number to set
	 */
	private void setRepeatCount(int rc) {
		this._repeatCount = rc;
	}

	/**
	 * This function is used to decrease the repeat count number
	 */
	private void decreaseRepeatCount() {
		_repeatCount--;
	}

	/**
	 * This function is used to initialize the animation of showing popupMessage
	 */
	private void initAnimation() {
		final TimingSource ts = new SwingTimerTimingSource();
		Animator.setDefaultTimingSource(ts);
		ts.init();
		checkAnimation();
		this._animator = new Animator.Builder()
				.setDuration(REPEAT_DURATION, TimeUnit.MILLISECONDS)
				.setRepeatCount(REPEAT_COUNT)
				.setRepeatBehavior(Animator.RepeatBehavior.REVERSE)
				.setStartDirection(Animator.Direction.BACKWARD)
				.addTarget(new TimingTargetAdapter() {
					@Override
					public void timingEvent(Animator animator, double fraction) {

						if (getRepeatCount() > (REPEAT_COUNT - OPACITY_INCREASE_COUNT)) {
							increaseOpacity();
						} else if (getRepeatCount() > (REPEAT_COUNT - OPACITY_DURATION_COUNT)) {
							;
						} else if (getRepeatCount() > (REPEAT_COUNT - OPACITY_DECREASE_COUNT)) {
							decreaseOpacity();
						} else {
							endAnimation();
						}
						decreaseRepeatCount();
					}
				}).build();
	}

	/**
	 * This function is used to start the animation of showing popupMessage
	 */
	private void startAnimation() {
		setMessageAlpha(0);
		setRepeatCount(REPEAT_COUNT);
		this._animator.start();
		this.setVisible(true);
	}

	/**
	 * This function is used to stop the animation of showing popupMessage
	 */
	private void endAnimation() {
		this._animator.stop();
		this.setVisible(false);
	}

	/**
	 * This function is used to check whether the animation is stopped
	 */
	private void checkAnimation() {
		if (_animator != null && _animator.isRunning()) {
			endAnimation();
		}
	}

	/**
	 * This function is used to increase the opacity of popupMessage
	 */
	private void increaseOpacity() {
		increaseAlpha();
		setMessageOpacity(getMessageAlpha());
	}

	/**
	 * This function is used to decrease the opacity of popupMessage
	 */
	private void decreaseOpacity() {
		decreaseAlpha();
		setMessageOpacity(getMessageAlpha());
	}

	/**
	 * This function is used to set the opacity of popupMessage
	 * 
	 * @param opacity
	 * 			opacity to set
	 */
	private void setMessageOpacity(float opacity) {
		AWTUtilities.setWindowOpacity(this, (float) (opacity / 256.0));
	}

	/**
	 * This function is used to set the location of popupMessage
	 * 
	 * @param x
	 * 		the x value of location
	 * @param y
	 * 		the y value of location
	 */
	public void setMessageLocation(int x, int y) {
		setLocation(new Point(x, y));
	}

	/**
	 * This function is used to re-layout the components of popupMessage
	 */
	public void reLayout() {
		int width = getTextWidth();
		if (width < DSPOPUPMESSAGE_TEXT_WIDTH_MINIMUM)
			width = DSPOPUPMESSAGE_TEXT_WIDTH_MINIMUM;

		this.setSize(width + _label.DSPOPUPMESSAGELABEL_TEXT_GAP_X * 2,
				DSPOPUPMESSAGE_TEXT_HEIGHT);
		this._label.setSise(width + _label.DSPOPUPMESSAGELABEL_TEXT_GAP_X * 2,
				DSPOPUPMESSAGE_TEXT_HEIGHT);
	}

	/**
	 * This function is used to get the text width of popupMessage
	 * 
	 * @return the text width
	 */
	private int getTextWidth() {
		return getFontMetrics(this.getFont())
				.stringWidth(this._label.getText());
	}
}

//@author A0103535R
/************************* Abstract Class DSPopupMessageLabel ***********************/
class DSPopupMessageLabel extends JLabel {
	private static final long serialVersionUID = 1L;
	protected final int DSPOPUPMESSAGELABEL_TEXT_GAP_X = 2;
	private int _width, _height;

	/**
	 * This is the constructor of DSPopupMessageLabel
	 */
	public DSPopupMessageLabel(String text) {
		super(text);
		init();
	}

	/**
	 * This function is used to initialize the label and set its appearance
	 */
	private void init() {
		setForeground(new Color(255, 255, 255));
		setFont(new Font("Arial", Font.PLAIN, 14));
		setHorizontalAlignment(JTextField.CENTER);
		setVerticalAlignment(JTextField.CENTER);
	}

	/**
	 * This function is used to set the size of label
	 * 
	 * @param w
	 * 		the width of label
	 * @param h
	 * 		the height of label
	 */
	public void setSise(int w, int h) {
		_width = w;
		_height = h;
		setSize(w, h);
	}

	@Override
	protected void paintComponent(Graphics g) {
		g.setColor(new Color(110, 110, 110, 200));
		g.fillRoundRect(0, 0, _width, _height, 20, 16);
		super.paintComponent(g);
	}
}